//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
// Portions Copyright (c) MiTAC Corp. All rights reserved.
//------------------------------------------------------------------------------
//
//  File:  main.c
//
//  This file implements main bootloader functions called from blcommon
//  library.
//
//  For BIN DIO download we are using workaround which allows flash image
//  larger than avaiable RAM memory. It depends on exact behavior of
//  BLCOMMON library.
//
//  Workaround depends on following sequence of call for each BIN record:
//  1 - OEMMapMemAddr, 2 - OEMReadData, 3 - OEMContinueEraseFlash.
//  First two calls store BIN record logical address and size. OEMMapMemAddr
//  returns each time same buffer address. OEMContinueEraseFlash then write
//  received BIN record to flash memory.
//
//  Flashing in OEMStartEraseFlash and OEMContinueEraseFlash expect all
//  BIN records are sector aligned. This is case for current version of
//  image update tools.
//
#include <eboot.h>
#include <boot_cfg.h>
#include <kitl_cfg.h>
#include <sermddapi.h>
#include <ipl_core.h>


//#undef OALMSG
//#define OALMSG(con,exp) (printk exp)
//------------------------------------------------------------------------------
//
//  Global: g_bootCfg
//
//  This global variable is used to save boot configuration. It is readed from
//  flash memory or initialized to default values if flash memory doesn't
//  contain valid structure. It can be modified by user in bootloader
//  configuration menu invoked by BLMenu.
//
BOOT_CFG g_bootCfg;

//------------------------------------------------------------------------------
//
//  Global: g_eboot
//
//  This global variable is used to save information about downloaded regions.
//
EBOOT_CONTEXT g_eboot;



//------------------------------------------------------------------------------
// External Functions

VOID JumpTo(UINT32 address);
VOID OEMDebugDeinit();
UINT32 ReadFlashIPL();
void _LedOn();
extern UINT16 USBpixels[];
extern UINT16 USBIpixels[];
extern BOOL OEMUSBWriteData(ULONG size, UCHAR *pData);
extern void OEMUSBprintk(LPCWSTR format, ...);
//------------------------------------------------------------------------------
//
//  Function:  OEMPlatformInit
//
//  This function provide platform initialization functions. It is called
//  from boot loader after OEMDebugInit is called.  Note that boot loader
//  BootloaderMain is called from  s/init.s code which is run after reset.
//

BOOL
OEMPlatformInit()
{

	//OALLog(L"hello\r\n");

	OALLog(
	    L"Microsoft EBOOT %d.%d for s3c2443 "
	    L"Built %s at %s\r\n",
	    EBOOT_VERSION_MAJOR, EBOOT_VERSION_MINOR, TEXT(__DATE__), TEXT(__TIME__)
	);

	//OALLog(L"hello2\r\n");

	//while(1);
	IPL_Core_Start();
	
	// Done
	InitMem();
	InitKeypad();
	return TRUE;
}

//------------------------------------------------------------------------------
//
//  Function:  OEMPreDownload
//
//  This function is called before downloading an image. There is place
//  where user can be asked about device setup.
//
ULONG
OEMPreDownload(
)
{
	ULONG rc = BL_ERROR;

	OALMSG(OAL_INFO,(L"INFO: Predownload....\r\n"));

	// We need to support multi bin notify
	g_pOEMMultiBINNotify = OEMMultiBinNotify;
#if 0
	// Read saved configration
	if (BLReadBootCfg(&g_bootCfg) &&
	        (g_bootCfg.signature == BOOT_CFG_SIGNATURE) &&
	        (g_bootCfg.version == BOOT_CFG_VERSION))
	{
		OALLog(L"INFO: Boot configuration found\r\n");
	}
	else
	{
		OALLog(L"WARN: Boot config wasn't found, using defaults\r\n");
		memset(&g_bootCfg, 0, sizeof(g_bootCfg));
		g_bootCfg.signature = BOOT_CFG_SIGNATURE;
		g_bootCfg.version = BOOT_CFG_VERSION;
		g_bootCfg.bootDevLoc.IfcType = Internal;
		g_bootCfg.bootDevLoc.BusNumber = 0;
		g_bootCfg.bootDevLoc.LogicalLoc = S3C2443_BASE_REG_PA_NAND + 0x10;
		g_bootCfg.kitlDevLoc.IfcType = Internal;
		g_bootCfg.kitlDevLoc.BusNumber = 0;
		g_bootCfg.kitlDevLoc.LogicalLoc = S3C2443_BASE_REG_PA_USB;
		g_bootCfg.kitlFlags |= 0;//OAL_KITL_FLAGS_DHCP| OAL_KITL_FLAGS_ENABLED;
		g_bootCfg.kitlFlags |= OAL_KITL_FLAGS_EXTNAME;
		g_bootCfg.ipAddress = 0x2100A8C0;
		g_bootCfg.ipMask = 0x00ffffff;
		g_bootCfg.ipRoute = 0;

	}
#else
	g_bootCfg.bootDevLoc.IfcType = Internal;
	g_bootCfg.bootDevLoc.LogicalLoc = KD_ID_USBSER;
#endif	
	// Call configuration menu
	BLMenu();
	// Image download depends on protocol
	g_eboot.bootDeviceType = OALKitlDeviceType( &g_bootCfg.bootDevLoc, g_bootDevices);

	switch (g_eboot.bootDeviceType)
	{
	//case OAL_KITL_TYPE_ETH:
	//	rc = BLEthDownload(&g_bootCfg, g_kitlDevices);
	//	break;
	//case OAL_KITL_TYPE_FLASH:
	//	rc = BLFlashDownload(&g_bootCfg, g_kitlDevices);
	//	break;
	case OAL_KITL_TYPE_SERIAL:
		//clear the screen
		LCDTermFillRect(0,0,CX_SCREEN,CY_SCREEN,0);
		SetCursorXY(0,0);
		LCDBitblt(USBIpixels,27);
		OEMUSBInit();
		LCDBitblt(USBpixels,27);
		g_eboot.readtotal=0;
		rc=BL_DOWNLOAD;
		break;
	}

	return rc;
}


//------------------------------------------------------------------------------
//
//  Function:  OEMLaunch
//
//  This function is the last one called by the boot framework and it is
//  responsible for to launching the image.
//
VOID
OEMLaunch(
    ULONG start,
    ULONG size,
    ULONG launch,
    const ROMHDR *pRomHeader
)
{
	//char buf[]={1,2,3,4,5,6};
	char buf[]="OEMLaunch: Transfer Complete\r\n";
#if 0
	BSP_ARGS *pArgs = OALPAtoCA(IMAGE_SHARE_ARGS_PA_START);
#endif
	OALMSGS(OAL_FUNC, (
	            L"+OEMLaunch(0x%08x, 0x%08x, 0x%08x, 0x%08x - %d/%d)\r\n", start, size,
	            launch, pRomHeader, g_eboot.bootDeviceType, g_eboot.type
	        ));
#if 0
	// Initialize ARGS structure
	if ((pArgs->header.signature != OAL_ARGS_SIGNATURE) ||
	        (pArgs->header.oalVersion != OAL_ARGS_VERSION) ||
	        (pArgs->header.bspVersion != BSP_ARGS_VERSION))
	{
		memset(pArgs, 0, IMAGE_SHARE_ARGS_SIZE);
		pArgs->header.signature = OAL_ARGS_SIGNATURE;
		pArgs->header.oalVersion = OAL_ARGS_VERSION;
		pArgs->header.bspVersion = BSP_ARGS_VERSION;
		pArgs->kitl.flags = g_bootCfg.kitlFlags;
		pArgs->kitl.devLoc = g_bootCfg.kitlDevLoc;
		pArgs->kitl.ipAddress = g_bootCfg.ipAddress;
		pArgs->kitl.ipMask = g_bootCfg.ipMask;
		pArgs->kitl.ipRoute = g_bootCfg.ipRoute;
		pArgs->imageLaunch = (UINT32)INVALID_HANDLE_VALUE;
	}

	// Depending on protocol there can be some action required
	switch (g_eboot.bootDeviceType)
	{
	case OAL_KITL_TYPE_ETH:
		BLEthConfig(pArgs);
		switch (g_eboot.type)
		{
		case DOWNLOAD_TYPE_BINDIO:
		case DOWNLOAD_TYPE_FLASHRAM:
			if (BLFlashDownload(&g_bootCfg, g_kitlDevices) != BL_JUMP)
			{
				OALMSG(OAL_ERROR, (L"ERROR: OEMLaunch: "
				                   L"Image load from flash memory failed\r\n"
				                  ));
				goto cleanUp;
			}
			launch = g_eboot.launchAddress;
			break;
		case DOWNLOAD_TYPE_EBOOT:
			OALLog(L"INFO: "
			       L"EBOOT downloaded, spin forever\r\n"
			      );
			while (TRUE)
				;
			break;
		case DOWNLOAD_TYPE_XLDR:
			OALLog(L"INFO: "
			       L"XLDR downloaded, spin forever\r\n"
			      );
			while (TRUE)
				;
			break;
		case DOWNLOAD_TYPE_IPL:
			OALLog(L"INFO: "
			       L"IPL downloaded, spin forever \r\n"
			      );
			//Roy                    ReadFlashIPL();
			//Roy      				launch = 0x13A20000;
			//Roy               while (TRUE);
			break;
		case DOWNLOAD_TYPE_UNKNOWN:
			launch = pArgs->imageLaunch;
			break;
		default:
			launch = (UINT32)OEMMapMemAddr(start, launch);
			break;
		}
		break;
	case OAL_KITL_TYPE_SERIAL:
		BLUSBClose();
		OALLog(L"INFO: downloaded, spin forever\r\n");
			while (TRUE);
		break;
	default:
		launch = g_eboot.launchAddress;
		break;
	}

	// Check if we get launch address
	if (launch == (UINT32)INVALID_HANDLE_VALUE)
	{
		OALMSG(OAL_ERROR, (L"ERROR: OEMLaunch: "
		                   L"Unknown image launch address, spin forever\r\n"
		                  ));
		while (TRUE)
			;
	}

	// Save launch address to arguments for reboot
	pArgs->imageLaunch = launch;
#endif
	// Print message, flush caches and jump to image
	OALLog(L"INFO: Lauch Windows CE .NET by jumping to 0x%08x...\r\n", launch);
	//OEMDebugDeinit();
	
	//LCD deinit and backlight off
	//SetBacklight(FALSE);
	//OMAPLCDOff();
	//OEMUSBDeinit();
	printk(L"INFO: Lauch Windows CE .NET by jumping to 0x%08x...\r\n", launch);
	switch(g_eboot.bootDeviceType)
	{
	case OAL_KITL_TYPE_ETH:
		//return EbootEtherReadData(size, pData);
	case OAL_KITL_TYPE_SERIAL:
		//while(1)
		OEMUSBWriteData(sizeof(buf),buf);
		//OEMUSBWriteData(sizeof(buf),buf);
		
	}
	//JumpTo(launch);

//cleanUp:
	return ;
}

//------------------------------------------------------------------------------
//
//  Function:   OEMMultiBinNotify
//
VOID
OEMMultiBinNotify(
    MultiBINInfo *pInfo
)
{
	BOOL rc = FALSE;
	UINT32 base = 0x31000000;
	UINT32 start, length;
	UINT32 ix;

	OALMSGS(OAL_FUNC, (
	            L"+OEMMultiBinNotify(0x%08x -> %d)\r\n", pInfo, pInfo->dwNumRegions
	        ));

	//Visual: dynamic alloc
	base=(UINT32)kmalloc(0);
	
	OALMSG(OAL_INFO, (
	           L"Download file information:\r\n"
	       ));
	OALMSG(OAL_INFO, (
	           L"-----------------------------------------------------------\r\n"
	       ));

	// Copy information to EBOOT structure and set also save address
	g_eboot.numRegions = pInfo->dwNumRegions;
	for (ix = 0; ix < pInfo->dwNumRegions; ix++)
	{
		g_eboot.region[ix].start = pInfo->Region[ix].dwRegionStart;
		g_eboot.region[ix].length = pInfo->Region[ix].dwRegionLength;
		g_eboot.region[ix].base = base;
		base += g_eboot.region[ix].length;
		OALMSG(OAL_INFO, (
		           L"[%d]: Address=0x%08x  Length=0x%08x  Save=0x%08x\r\n",
		           ix, g_eboot.region[ix].start, g_eboot.region[ix].length,
		           g_eboot.region[ix].base
		       ));
	}
	OALMSG(OAL_INFO, (
	           L"-----------------------------------------------------------\r\n"
	       ));

	// Determine type of image downloaded
	if (g_eboot.numRegions > 1)
	{
		OALMSG(OAL_ERROR, (L"ERROR: MultiXIP image is not supported\r\n"));
		goto cleanUp;
	}

	base = g_eboot.region[0].base;
	start = g_eboot.region[0].start;
	length = g_eboot.region[0].length;
	// Roy
	OALLog(L"Start address = 0x%x, length = 0x%x, base = 0x%x \r\n", start, length, base);
	
	if ((start < 0x10000000))//flash.bin
	{
		g_eboot.type = DOWNLOAD_TYPE_BINDIO;
		memset((VOID*)base, 0xFF, 0x03000000);
	}
	//else if (start == IMAGE_STEP_ADDR_PA)
	//{
	//	g_eboot.type = DOWNLOAD_TYPE_XLDR;
	//	memset((VOID*)g_eboot.region[0].base, 0xFF, g_eboot.region[0].length);
	//}
	else if ((start == 0x30038000))//myEboot
	{
		g_eboot.type = DOWNLOAD_TYPE_EBOOT;
		memset((VOID*)base, 0xFF, length);
	}
	else if (start == 0x30000000)//steploader+ipl+eboot
	{
		g_eboot.type = DOWNLOAD_TYPE_BOOTLOADER;
		memset((VOID*)base, 0x0, length);
	}
	else
	{
		g_eboot.type = DOWNLOAD_TYPE_UNKNOWN;
	}	
	OALLog(L"g_eboot.type = %d \r\n", g_eboot.type);
	rc = TRUE;

cleanUp:
	if (!rc)
	{
		OALMSG(OAL_ERROR, (L"Spin for ever...\r\n"));
		while (TRUE)
			;
	}
	OALMSGS(OAL_FUNC, (L"-OEMMultiBinNotify\r\n"));
}

//------------------------------------------------------------------------------
//
//  Function:  OEMMapMemAddr
//
//  This function maps image relative address to memory address. It is used
//  by boot loader to verify some parts of downloaded image.
//
//  For OMAP730 EBOOT mapping depends on download type. Download type is
//  set in OMEMultiBinNotify.
//
UINT8*
OEMMapMemAddr(
    DWORD image,
    DWORD address
)
{
	UINT8 *pAddress = NULL;

	OALMSG(OAL_FUNC, (L"+OEMMapMemAddr(0x%08x, 0x%08x)\r\n", image, address));
	switch (g_eboot.type)
	{
	case DOWNLOAD_TYPE_BINDIO:
		g_eboot.recordOffset = address - image;
		//pAddress = (UINT8*)g_eboot.region[0].base;
		pAddress = (UINT8*)(g_eboot.region[0].base + address - image);
		break;
	case DOWNLOAD_TYPE_XLDR:
	case DOWNLOAD_TYPE_EBOOT:
	case DOWNLOAD_TYPE_IPL:
	case DOWNLOAD_TYPE_BOOTLOADER:
		pAddress = (UINT8*)(g_eboot.region[0].base + address - image);
		break;
	default:
		OALMSG(OAL_ERROR, (L"ERROR: OEMMapMemAddr: "
		                   L"Invalid download type %d\r\n", g_eboot.type
		                  ));
		break;
	}

	OALMSGS(OAL_FUNC, (L"-OEMMapMemAddr(pAddress = 0x%08x)\r\n", pAddress));
	return pAddress;
}

//------------------------------------------------------------------------------
//
//  Function:  OEMIsFlashAddr
//
//  This function determines whether the address provided lies in a platform's
//  flash or RAM address range.
//
//  For OMAP730 EBOOT decision depends on download type. Download type is
//  set in OMEMultiBinNotify.
//
BOOL
OEMIsFlashAddr(
    ULONG address
)
{
	BOOL rc;

	//   OALMSG(OAL_FUNC, (L"+OEMIsFlashAddr(0x%08x)\r\n", address));
	OALLog(L"+OEMIsFlashAddr(0x%08x)\r\n", address);
	// Depending on download type
	switch (g_eboot.type)
	{
	case DOWNLOAD_TYPE_BINDIO:
	case DOWNLOAD_TYPE_XLDR:
	case DOWNLOAD_TYPE_EBOOT:
	case DOWNLOAD_TYPE_IPL:
	case DOWNLOAD_TYPE_BOOTLOADER:
		rc = TRUE;
		break;
	default:
		rc = FALSE;
		break;
	}

	//    OALMSG(OAL_FUNC, (L"-OEMIsFlashAddr(rc = %d)\r\n", rc));
	OALLog(L"-OEMIsFlashAddr(rc = %d)\r\n", rc);
	return rc;
}

//------------------------------------------------------------------------------
//
//  Function:  OEMShowProgress
//
//  This function is called during the download process to visualise
//  download progress.
//
VOID OEMShowProgress(ULONG packetNumber)
{
#ifndef EBOOT_NO_LCD
	UINT32 width=packetNumber;
	width=(g_eboot.readtotal>>8)*(CX_SCREEN-8)/(g_eboot.region[0].length>>8);

	LCDTermFillRect(4, CY_SCREEN-10-2, width, 10, 0xF000);
#endif
}

#if 0
//  Function:  OEMStartEraseFlash
//
//  This function is called by the bootloader to initiate the flash memory
//  erasing process.
//
BOOL OEMStartEraseFlash(ULONG address, ULONG size)
{
	BOOL rc = TRUE;
#if 0
	switch (g_eboot.type)
	{
	case DOWNLOAD_TYPE_BINDIO:
		rc = StartWriteFlashBinDIO(address, size);
		break;
	}
#endif
	return rc;
}


//------------------------------------------------------------------------------
//
//  Function:  OEMContinueEraseFlash
//
//  This function is called by the bootloader to during download to provide
//  ability to continue the flash block erasing operation.
//
VOID OEMContinueEraseFlash()
{
#if 0
	switch (g_eboot.type)
	{
	case DOWNLOAD_TYPE_BINDIO:
		ContinueWriteFlashBinDIO(
		    g_eboot.recordOffset, g_eboot.pReadBuffer, g_eboot.readSize
		);
		break;
	}
#endif	
}


//------------------------------------------------------------------------------
//
//  Function:  OEMFinishEraseFlash
//
//  This function is called by the bootloader to finish flash erase before
//  it will call OEMWriteFlash.
//
//  On OMAP730 we erase flash as part of OEMWriteFlash.
//
BOOL
OEMFinishEraseFlash()
{
	BOOL rc = TRUE;
#if 0
	switch (g_eboot.type)
	{
	case DOWNLOAD_TYPE_BINDIO:
		rc = FinishFlashBinDIO();
		break;
	}
#endif
	return rc;
}
//------------------------------------------------------------------------------
//
//  Function:  OEMWriteFlash
//
//  This function is called by the bootloader to write the image that may
//  be stored in a RAM file cache area to flash memory. This function is
//  called once per each downloaded region.
//
BOOL
OEMWriteFlash(ULONG address, ULONG size)
{
	BOOL rc;
	OALMSG(OAL_FUNC, (L"+OEMWriteFlash(0x%08x)\r\n", address));
	//printk(L"+OEMWriteFlash(0x%08x)\r\n", address);

	return TRUE;
}
#endif
BOOL OEMReadData(ULONG size, UCHAR *pData)
{
	// Save read data size and location. It is used in workaround
	// for download BIN DIO images larger than RAM.
	g_eboot.readSize = size;
	g_eboot.pReadBuffer = pData;
	g_eboot.readtotal+=g_eboot.readSize;

	switch(g_eboot.bootDeviceType)
	{
	case OAL_KITL_TYPE_ETH:
		//return EbootEtherReadData(size, pData);
	case OAL_KITL_TYPE_SERIAL:
		return OEMUSBReadData(size,pData);
	default:
		return FALSE;
	}
}

UINT32 OALGetTickCount()
{
    static ULONG count = 0;

    count++;
    return count/100;
}

//------------------------------------------------------------------------------
VOID* OALPAtoVA(UINT32 pa, BOOL cached)
{
    return (void *)pa;
}